package services

import (
	"fmt"
	"path/filepath"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/fatih/color"

	"github.com/deploy-services/v2/filesystem"
	"github.com/deploy-services/v2/kcfg"
	"github.com/deploy-services/v2/log"
)

var global_mnodes mnodes
var global_services services
var global_cfg *kcfg.Cfg
var global_publics publics
var global_pacakged packaged
var global_environments environments
var global_configures configures

func Use(file string) {
	filesystem.SetConfig(file)
	Reload()
}

func Reload() {
	cfg := kcfg.NewCfg()
	cfg.ParseFile(filesystem.Config())
	local_data := cfg.Child("local_data").GetString()
	global_dir := global_cfg.Child("local_data").GetString()
	if local_data != global_dir {
		filesystem.RecordDir = strings.Replace(filesystem.RecordDir, global_dir, local_data, 1)
		filesystem.DataDir = strings.Replace(filesystem.DataDir, global_dir, local_data, 1)
		filesystem.PackagedDir = strings.Replace(filesystem.PackagedDir, global_dir, local_data, 1)

		filesystem.MkDirs(filesystem.RecordDir)
		filesystem.MkDirs(filesystem.DataDir)
		filesystem.MkDirs(filesystem.PackagedDir)
	}

	global_cfg = cfg
	// 加载所有服务器节点信息
	global_mnodes = parseMNodes(global_cfg.Childs("node"))
	// 加载所有服务信息
	global_pacakged = parsePackaged(global_cfg.Child("package"))
	// 加载配置
	global_configures = parseConfigures(global_cfg.Childs("configure"))
	// 加载所有环境变量信息
	global_environments = parseEnvironments(global_cfg.Childs("environment"))
	global_publics = parsePublics(global_cfg.Childs("public"))
	global_publics.updateNodes(global_mnodes)
	global_services = parseServices(global_cfg.Childs("service"))
	// global_services.updateNodes(global_mnodes)

	RefreshLocalDependsFiles()
}

func LoadConfig() {
	cfg := kcfg.NewCfg()
	cfg.ParseFile(filesystem.Config())
	local_data := cfg.Child("local_data").GetString()
	if len(local_data) > 0 {
		filesystem.RecordDir = filepath.Join(local_data, filesystem.RecordDir)
		filesystem.DataDir = filepath.Join(local_data, filesystem.DataDir)
		filesystem.PackagedDir = filepath.Join(local_data, filesystem.PackagedDir)
	}
	filesystem.MkDirs(filesystem.RecordDir)
	filesystem.MkDirs(filesystem.DataDir)
	filesystem.MkDirs(filesystem.PackagedDir)

	global_cfg = cfg
	// 加载所有服务器节点信息
	global_mnodes = parseMNodes(global_cfg.Childs("node"))
	// 加载所有服务信息
	global_pacakged = parsePackaged(global_cfg.Child("package"))
	// 加载配置
	global_configures = parseConfigures(global_cfg.Childs("configure"))
	// 加载所有环境变量信息
	global_environments = parseEnvironments(global_cfg.Childs("environment"))
	global_publics = parsePublics(global_cfg.Childs("public"))
	global_publics.updateNodes(global_mnodes)
	global_services = parseServices(global_cfg.Childs("service"))
	// global_services.updateNodes(global_mnodes)
}

func RefreshLocalDependsFiles() {
	// 开始遍历本地 data 目录所有文件
	path, _ := filesystem.TraverveDir(filesystem.DataDir)
	// 更新文件依赖
	global_services.updateDependsFiles(path)
	// 获取所有的文件依赖项，并计算文件的MD5值
	all_depends := global_services.getAllDepends()
	all_depends.RefreshMD5()
}

func ShowDiff() string {
	// 取出当前版本的文件依赖属性
	global_services.loadActiveDepends()
	// 取出所有active 依赖节点
	all_active_depends := global_services.getAllActiveDepends()
	// 开始对比当前文件夹下文件和活跃版本的文件，获取到需要更新的内容
	diff_update := make(filesystem.Files, 0)
	diff_new := make(filesystem.Files, 0)
	all_depends := global_services.getAllDepends()
	for _, v1 := range all_depends {
		find := false
		for _, v2 := range all_active_depends {
			if v1.Path() == v2.Path() {
				if v1.MD5() == v2.MD5() {
					find = true
				} else {
					diff_update = append(diff_update, v1)
				}
			}
		}
		if !find {
			diff_new = append(diff_new, v1)
		}
	}

	var rest string
	rest += fmt.Sprintln("############################################################################")
	rest += fmt.Sprintf("New List: %v\n", len(diff_new))
	for _, n := range diff_new {
		rest += fmt.Sprintf("\t%v\t%v\t<%v>\n", n.Path(), n.IsDir(), n.MD5())
	}

	rest += fmt.Sprintf("Update List:%v\n", len(diff_update))
	for _, n := range diff_update {
		rest += fmt.Sprintf("\t%v\t%v\t<%v>\n", n.Path(), n.IsDir(), n.MD5())
	}
	rest += fmt.Sprintln("############################################################################")
	return rest
}

// func ShowServices() {
// 	log.Printf("\n%v\n", global_services.dump())
// }

// func ShowNodes() {
// 	log.Printf("\n%v\n", global_mnodes.dump(""))
// }

//	func Commit() {
//		global_pacakged.commit()
//		global_publics.commit()
//		// 提交本地更新内容，并上传到远端
//		global_services.commit()
//	}
func ServiceShow(name string) string {
	return global_services.dump(name)
}

func ServiceCommit(name string) string {
	names, rests, errs := global_services.commit(name)
	var bs strings.Builder
	w := tabwriter.NewWriter(&bs, 10, 0, 2, ' ', 0)
	fmt.Fprintln(w, "Name\tCommit\tErr")
	fmt.Fprintln(w, "----\t------\t---")
	for i, v := range names {
		fmt.Fprintf(w, "%v\t%v\t%v\n", v, rests[i], errs[i])
	}
	w.Flush()
	return bs.String()
}

func PublicCommit(name string, force bool) string {
	names, rests, errs := global_publics.commit(name, force)
	var bs strings.Builder
	w := tabwriter.NewWriter(&bs, 10, 0, 2, ' ', 0)
	fmt.Fprintln(w, "Name\tCommit\tErr")
	fmt.Fprintln(w, "----\t------\t---")
	for i, v := range names {
		fmt.Fprintf(w, "%v\t%v\t%v\n", v, rests[i], errs[i])
	}
	w.Flush()
	return bs.String()
}

func PublicShow(name string) string {
	return global_publics.dump(name)
}

func PublicActive(name string, commit string) string {
	names, rests, errs := global_publics.active(name, commit)
	var bs strings.Builder
	w := tabwriter.NewWriter(&bs, 10, 0, 2, ' ', 0)
	fmt.Fprintln(w, "Name\tCommit\tErr")
	fmt.Fprintln(w, "----\t------\t---")
	for i, v := range names {
		fmt.Fprintf(w, "%v\t%v\t%v\n", v, rests[i], errs[i])
	}
	w.Flush()
	return bs.String()
}

func ServiceActive(name string, commit string, force bool) string {
	names, rests, errs := global_services.active(name, commit, force)
	var bs strings.Builder
	w := tabwriter.NewWriter(&bs, 10, 0, 2, ' ', 0)
	fmt.Fprintln(w, "Name\tCommit\tErr")
	fmt.Fprintln(w, "----\t------\t---")
	for i, v := range names {
		fmt.Fprintf(w, "%v\t%v\t%v\n", v, rests[i], errs[i])
	}
	w.Flush()
	return bs.String()
}

// func Active(ver string) {
// 	global_publics.active(ver)
// 	global_services.active(ver, false)
// }

func ShowServiceHistory(service string) string {
	return global_services.show_history(service)
}

func ShowPublicHistory(name string) string {
	return global_publics.show_history(name)
}

func ShowConfig() {
	log.Println(global_configures.dump())
}

//	func getConsoleWidth() int {
//		width, _, err := term.GetSize(int(os.Stdout.Fd()))
//		if err != nil {
//			return 10
//		}
//		return width
//	}
func getStringMaxLineSize(str string) int {
	strs := strings.Split(str, "\n")
	max := 0
	for _, s := range strs {
		if len(s) > max {
			max = len(s)
		}
	}
	return max
}

func OutputFormat(nodes, rlts []string, errs []error) string {
	max_char := 10
	var bs strings.Builder
	for i, n := range nodes {
		fmt.Fprintln(&bs, n+":")
		fmt.Fprintln(&bs, "@@----------@@")
		var str string
		str = fmt.Sprintln("Stdout:")
		str += fmt.Sprintln(strings.Trim(rlts[i], "\r\n"))
		if errs[i] != nil {
			str += fmt.Sprintln()
			str += fmt.Sprintln("Stderr:")
			str += fmt.Sprint(errs[i])
		}
		sz := getStringMaxLineSize(str)
		if sz > max_char {
			max_char = sz
		}
		fmt.Fprintln(&bs, strings.Trim(str, "\r\n"))
		fmt.Fprintln(&bs, "@@==========@@")
		fmt.Fprintln(&bs)
		// fmt.Fprintln(&bs, strings.Repeat("-", helper.GetConsoleWidth()-10))
		// if
	}

	res := bs.String()
	res = strings.ReplaceAll(res, "@@----------@@", strings.Repeat("-", max_char))
	res = strings.ReplaceAll(res, "@@==========@@", strings.Repeat("=", max_char))
	return res
}

func ServiceStart(service, node string) string {
	nodes, rlts, errs := global_services.service_start(service, node)
	return OutputFormat(nodes, rlts, errs)
}

func ServiceStop(service, node string) string {
	nodes, rlts, errs := global_services.service_stop(service, node)
	return OutputFormat(nodes, rlts, errs)
}
func is_health(str string) (bool, error) {
	str = strings.Trim(str, "\r\n \t")
	n, e := strconv.ParseInt(str, 10, 10)
	if e == nil {
		if n > 0 {
			return true, nil
		} else {
			return false, nil
		}
	}

	return false, e
}

func ServiceHealth(service, node string) string {
	nodes, rlts, errs := global_services.service_health(service, node)
	var bs strings.Builder
	w := tabwriter.NewWriter(&bs, 10, 0, 2, ' ', 0)
	fmt.Fprintln(w, "Node\tHealth\tErr")
	fmt.Fprintln(w, "----\t------\t---")
	for i, n := range nodes {
		c1 := n
		c2 := strings.Trim(rlts[i], "\r\n \t")
		c3 := fmt.Sprint(errs[i])
		if len(c2) > 0 {
			n, e := strconv.ParseInt(c2, 10, 10)
			if e == nil {
				if n > 0 {
					c2 = color.GreenString("Online")
				} else {
					c2 = color.RedString("Offline")
				}
			}
		}
		fmt.Fprintf(w, "%s\t%s\t%s\n", c1, c2, c3)
	}
	w.Flush()
	return bs.String()
}

func ServiceCMD(service, node string, cmd string) string {
	nodes, rlts, errs := global_services.run_cmd(service, node, command{bind_env: false, cmd: cmd})
	return OutputFormat(nodes, rlts, errs)
}

func ServicePush(service, node, file string, content []byte) string {
	nodes, rlts, errs := global_services.service_push(service, node, file, content)
	var bs strings.Builder
	w := tabwriter.NewWriter(&bs, 10, 0, 2, ' ', 0)
	fmt.Fprintln(w, "Node\tStatus\tErr")
	fmt.Fprintln(w, "----\t------\t---")
	for i, n := range nodes {
		c1 := n
		c2 := strings.Trim(rlts[i], "\r\n \t")
		c3 := fmt.Sprint(errs[i])
		if len(c2) > 0 {
			n, e := strconv.ParseInt(c2, 10, 10)
			if e == nil {
				if n > 0 {
					c2 = color.GreenString("Online")
				} else {
					c2 = color.RedString("Offline")
				}
			}
		}
		fmt.Fprintf(w, "%s\t%s\t%s\n", c1, c2, c3)
	}
	w.Flush()
	return bs.String()
}

func PackagePush(file string, content []byte) string {
	err := push(file, content)
	if err != nil {
		return err.Error()
	}
	return "OK"
}

func NodesShow() string {
	return global_mnodes.dump()
}

func NodeCMD(name string, cmd string) string {
	nodes, rlts, errs := global_mnodes.run_cmd(name, command{bind_env: false, cmd: cmd})
	var bs strings.Builder
	w := tabwriter.NewWriter(&bs, 10, 0, 2, ' ', 0)
	fmt.Fprintln(w, "Node\tHost\tResult\tErr")
	fmt.Fprintln(w, "----\t----\t------\t---")
	for i, n := range nodes {
		if errs[i] == nil && len(rlts[i]) > 0 {
			lines := strings.Split(rlts[i], "\n")
			for j, l := range lines {
				l = strings.ReplaceAll(l, "\r", "")
				l = strings.ReplaceAll(l, "\t", "  ")
				if j == 0 {
					fmt.Fprintf(w, "%v\t%v\t%v\t\n", n.name, n.host, l)
				} else {
					fmt.Fprintf(w, "\t\t%v\t\n", l)
				}
			}
		} else {
			fmt.Fprintf(w, "%v\t%v\t%v\t%v\n", n.name, n.host, rlts[i], errs[i])
		}
	}

	w.Flush()
	return bs.String()
}

func NodeEnable(node_str string, state bool) string {
	global_mnodes.enable(node_str, state)
	return NodesShow()
}

func PackageCommit() string {
	// global_pacakged.commit()
	// return "Completed"

	files, errs := global_pacakged.commit()
	var bs strings.Builder
	w := tabwriter.NewWriter(&bs, 10, 0, 2, ' ', 0)
	fmt.Fprintln(w, "File\tErr")
	fmt.Fprintln(w, "----\t---")
	for i, n := range files {
		if errs[i] == nil {
			fmt.Fprintf(w, "%v\tSuccess\n", n)
		} else {
			fmt.Fprintf(w, "%v\t%v\n", n, errs[i])
		}
	}

	w.Flush()
	return bs.String()
}

func Sync() string {
	// package commit
	var bs strings.Builder
	fmt.Fprintln(&bs, "Package Commit Result:")
	fmt.Fprintln(&bs, PackageCommit())

	RefreshLocalDependsFiles()

	fmt.Fprintln(&bs, "Public Commit Result:")
	fmt.Fprintln(&bs, PublicCommit("", true))

	fmt.Fprintln(&bs, "Public Active Result:")
	fmt.Fprintln(&bs, PublicActive("", "latest"))

	fmt.Fprintln(&bs, "Service Commit Result:")
	fmt.Fprintln(&bs, ServiceCommit(""))

	fmt.Fprintln(&bs, "Service Active Result:")
	fmt.Fprintln(&bs, ServiceActive("", "latest", true))

	return bs.String()
}

func DaemonStatus(name string) string {
	names, n_names, hosts, bases, stats := global_daemon_manager.status(name)
	var bs strings.Builder
	w := tabwriter.NewWriter(&bs, 10, 0, 2, ' ', 0)
	fmt.Fprintln(w, "S-Name\tN-Name\tHost\tBase\tStatus")
	fmt.Fprintln(w, "------\t------\t----\t----\t------")
	for i, n := range names {
		if stats[i] {
			fmt.Fprintf(w, "%v\t%v\t%v\t%v\tActive\n", n, n_names[i], hosts[i], bases[i])
		} else {
			fmt.Fprintf(w, "%v\t%v\t%v\t%v\tInactive\n", n, n_names[i], hosts[i], bases[i])
		}
	}

	w.Flush()
	return bs.String()
}

func DaemonStart(name string) string {
	global_daemon_manager.start(name)
	time.Sleep(1 * time.Second)
	return DaemonStatus(name)
}

func DaemonStop(name string) string {
	global_daemon_manager.stop(name)
	time.Sleep(1 * time.Second)
	return DaemonStatus(name)
}

func Load() {
	LoadConfig()
	RefreshLocalDependsFiles()
}
